THRIFT-6056: Limit recursion depth in Dart struct read/write#3561
Merged
Conversation
103cd55 to
61ab25f
Compare
Client: dart
Add a _recursionDepth counter with incrementRecursionDepth() and
decrementRecursionDepth() to TProtocol, bounded at 64, and update the
Dart generator to bracket each generated struct read/write body with
try/finally so the counter is always restored. This bounds the work
performed for deeply nested or cyclic structs, which previously had no
limit in the generated read()/write() path. Unions and exceptions are
generated through the same read/write path, so they are bounded too.
Add a regression test under test/dart/recursion_depth_test that drives
the recursive IDL types from test/Recursive.thrift through the generated
read()/write() over the binary, compact and JSON protocols:
- struct chains at the limit round-trip; chains one past it are rejected
with DEPTH_LIMIT on both write and read,
- the recursive exception CoError/CoError2 likewise round-trips at the
limit and is rejected one past it on both write and read,
- a wide (shallow) tree confirms decrementRecursionDepth() unwinds each
sibling back to depth 1,
- a cyclic object graph is rejected instead of recursing without bound.
The over-limit read payloads are hand-serialized with the real recursive
field (id 1, type STRUCT) so the reader recurses through the guarded
generated read(), not skip().
The test needs a null-safe Dart SDK (>= 2.12); generation and execution
are wired via `make recursion-test` and kept out of the default cross
chain (the cross images currently pin an older Dart).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61ab25f to
f9930ab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
THRIFT-6056: Limit recursion depth in the Dart library
Change
Adds a
_recursionDepthcounter (incrementRecursionDepth()/decrementRecursionDepth()) toTProtocol, bounded at 64, and updates the Dart generator to bracket each generated structread/writebody withtry/finallyso the counter is always restored. Previously the generatedread()/write()path had no limit. Unions and exceptions are generated through the same path, so they are bounded too.Test
A regression test under
test/dart/recursion_depth_testdrives the recursive IDL types fromtest/Recursive.thriftthrough the generatedread()/write()over Binary, Compact and JSON:CoRec— chains one below / at the limit round-trip; one past it is rejected withDEPTH_LIMITon both write and read; a wide shallow tree round-trips (the counter unwinds per sibling); a cyclic graph is rejected.CoError/CoError2— round-trips at the limit and is rejected one past it on both write and read.Over-limit read payloads are hand-serialized with the real recursive field (id 1, type
STRUCT) so the reader recurses through the guarded generatedread(), notskip()(Dart'sskip()is not depth-bounded, so a mis-routed payload would not raiseDEPTH_LIMIT— the passing read tests confirm correct routing).The test needs a null-safe Dart SDK (>= 2.12); generation/execution are wired via
make recursion-testand kept out of the default cross chain (the cross images pin an older Dart). Validated locally on Dartstable(3.x): 27/27 pass.🤖 Generated with Claude Code